package net.cloudsun.graph.puzzle;

import net.cloudsun.graph.util.ArrayUtils;

import java.io.Serializable;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 拼图状态
 *
 * @author Ryan
 * @since 1.1
 */
public class JigsawState implements Serializable, Comparable<JigsawState> {

    private static final String[] CHARS = {"↖ ", "㊤", "↗ ", "㊧", "㊥", "㊨", "↙ ", "㊦", "〓"};
    private static final String[] NUMBERS = {"1", "2", "3", "4", "5", "6", "7", "8", "9"};

    /**
     * 状态
     */
    private byte[] state = new byte[5];

    public JigsawState(String s) {
        List<Integer> integers = toIntegers(s);
        saveState(integers);
    }

    public JigsawState(Collection<Integer> integers) {
        saveState(integers);
    }


    public JigsawState(int[][] ints) {
        int[] numbers = ArrayUtils.joinArray(ints);
        saveState(numbers);
    }

    private void saveState(int[] integers) {
        List<Integer> list = Arrays.stream(integers).mapToObj(Integer::new).collect(Collectors.toList());
        saveState(list);
    }

    private void saveState(Collection<Integer> integers) {
        int i = 0;
        for (int n : integers) {
            // 高位
            if ((i & 0x1) == 0) {
                state[i >> 1] |= n << 4;
            } else {
                state[i >> 1] |= n;
            }
            i++;
        }
    }


    @Override
    public String toString() {
        /*
         *
         * ↖ ㊤ ↗
         * ㊧ ㊥ ㊨
         * ↙ ㊦ ↘
         * @return
         */
        // 每个byte可以显示两个格子的数字
        int[][] ints = this.toArray();
        return getString(ints);
    }

    public String toSingleLineString() {
        int[] ints = ArrayUtils.joinArray(this.toArray());
        // 把这些全部转为字符串
        // 然后按行存储
        String line = String.join("", Arrays.stream(ints).mapToObj(String::valueOf).
                collect(Collectors.toList()));
        return line;
    }

    public String toNumberString() {
        int[][] ints = this.toArray();
        return getString(ints, NUMBERS);
    }


    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (!(o instanceof JigsawState)) {
            return false;
        }
        JigsawState that = (JigsawState) o;
        return Arrays.equals(state, that.state);
    }

    @Override
    public int hashCode() {
        return Arrays.hashCode(state);
    }


    public int[][] toArray() {
        int[][] array = new int[3][3];
        int count = 0;
        int row = 0;
        int column = 0;
        for (byte b : state) {
            int[] numbers = getNumbers(b);
            for (int i = 0; i < numbers.length && count < 9; i++) {
                int number = numbers[i];
                count++;
                array[row][column++] = number;
                if (column == 3) {
                    row++;
                    column = 0;
                }
            }
        }
        return array;

    }

    public int[] toPlainArray() {
        int[] array = new int[9];
        int count = 0;
        int column = 0;
        for (byte b : state) {
            int[] numbers = getNumbers(b);
            for (int i = 0; i < numbers.length && count < 9; i++) {
                int number = numbers[i];
                count++;
                array[column++] = number;
            }
        }
        return array;

    }

    public Map<PuzzleDirection,JigsawState> getBrothers() {
        /*
         * 将state换成二维数组
         * 比如这种
         * 1 2 3
         * 4 5 7
         * 7 8 0
         * 比如两个拼图，以8为可活动节点
         * 节点A     节点B
         * 1 2 3    1 2 3
         * 4 5 6 -> 4 8 6
         * 7 8 0    7 5 0
         * 这两个要画一条边
         * 如果8在中央有4个边 1 1
         * 如果8在方阵四个中点上，有3个边 0 1, 1 0, 1 2, 2 1
         * 如果8在角落，只有两个边 0 0, 0 2, 2 0, 2 2
         */
        int[][] ints = this.toArray();
        final Pair<Integer> location = PuzzleStateRelation.getLocation(this, 8);
        int row = location.getA();
        int column = location.getB();
        // 首先中央吧
        if (row == 1 && column == 1) {
            return PuzzleStateRelation.brothersByCenter(ints, row, column);
        } else if (row - column == 1 || row - column == -1) {
            // 再是四边的中点 0 1, 1 0, 1 2, 2 1
            return PuzzleStateRelation.brothersByEdgeCenter(ints, row, column);
        } else {
            // 最后是四个角
            return PuzzleStateRelation.brothersByCorner(ints, row, column);
        }
    }

    private static int[] getNumbers(byte b) {
        return new int[]{(b & 0xF0) >>> 4, b & 0x0f};
    }

    private static String getString(int[][] ints) {
        String[] chars = NUMBERS;
        return getString(ints, chars);
    }

    private static String getString(int[][] ints, String[] chars) {
        StringBuilder sb = new StringBuilder();
        Arrays.stream(ints).forEach(x -> {
            List<String> strings = Arrays.stream(x).mapToObj(i -> chars[i]).collect(Collectors.toList());
            String line = String.join(" ", strings);
            sb.append(line).append("\n");
        });
        return sb.toString();
    }

    private static List<Integer> toIntegers(String s) {
        char[] chars = s.toCharArray();
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < chars.length; i++) {
            char c = chars[i];
            int n = c - '0';
            list.add(n);
        }
        return list;
    }

    @Override
    public int compareTo(JigsawState o) {
        return this.toSingleLineString().compareTo(o.toSingleLineString());
    }
}
